package cn.ztydata.teachingsystem.heaven.service;

import cn.ztydata.teachingsystem.common.Page;
import cn.ztydata.teachingsystem.exception.ServiceException;
import cn.ztydata.teachingsystem.heaven.common.WebContext;
import cn.ztydata.teachingsystem.heaven.cons.Roles;
import cn.ztydata.teachingsystem.heaven.dao.*;
import cn.ztydata.teachingsystem.heaven.entity.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.util.*;

/**
 * 课程服务类
 * Created by chenxin on 2014/12/1.
 */
@Service
public class CourseService extends BaseService {
    @Autowired
    private ResourceService resourceService;
    @Autowired
    private TeacherTeamService teacherTeamService;
    @Autowired
    private CourseDao courseDao;
    @Autowired
    private TeacherTeamDao teacherTeamDao;
    @Autowired
    private CourseOverviewDao courseOverviewDao;
    @Autowired
    private CourseCodeDao courseCodeDao;
    @Autowired
    private SpecialtyDao specialtyDao;
    @Autowired
    private DepartmentDao departmentDao;
    @Autowired
    private ChapterDao chapterDao;
    @Autowired
    private TeacherDao teacherDao;
    @Autowired
    private StudentCourseDao studentCourseDao;
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private CourseGroupDao courseGroupDao;

    @Autowired
    private WebContext webContext;

    /**
     * 按照系部统计课程总数
     * @return map
     *
     * @author cx
     * @since 2014-12-01
     */
    public Map<Long, Long> getCourseTotalForDept() {
        //获取系部的课程总数
        List<Map<String, Object>> courseTotalList = courseDao.getTotalForDept();

        //将统计信息转换为“系部ID->课程总量”格式
        Map<Long, Long> deptToCourse = Maps.newHashMap();
        log.debug("循环转换系部课程统计格式");
        for (Map<String, Object> courseTotal : courseTotalList) {
            deptToCourse.put((Long)courseTotal.get("deptId"), (Long)courseTotal.get("total"));
        }

        log.debug("return 系部课程统计信息");
        return deptToCourse;
    }

    /**
     * 按照专业统计课程总数
     * @param deptId 系部ID
     * @return map
     *
     * @author cx
     * @since 2014-12-02
     */
    public Map<Long, Long> getCourseTotalForSpec(final long deptId) {
        //获取专业的课程总数
        List<Map<String, Object>> courseTotalList = courseDao.getTotalForSpec(deptId);

        //将统计信息转换为“专业ID->课程总量”格式
        Map<Long, Long> specToCourse = Maps.newHashMap();
        log.debug("转换专业课程统计格式");
        for (Map<String, Object> courseTotal : courseTotalList) {
            specToCourse.put((Long)courseTotal.get("specId"), (Long)courseTotal.get("total"));
        }

        log.debug("return 专业课程统计map");
        return specToCourse;
    }

    /**
     * 统计教师建课数量
     * @param teacherIdList 教师ID集合
     * @return Map
     *
     * @author cx
     * @since 2014-12-03
     */
    public Map<Long, Long> getCourseTotalForTeacher(final List<Long> teacherIdList) {
        //获取教师的建课总数
        List<Map<String, Object>> courseTotalList = courseDao.getTotalForTeacher(teacherIdList);

        //将统计信息转换为“教师ID->课程总量”格式
        Map<Long, Long> buildCourse = Maps.newHashMap();
        log.debug("循环转换教师建课统计格式");
        for (Map<String, Object> courseTotal : courseTotalList) {
            buildCourse.put((Long)courseTotal.get("teacherId"), (Long)courseTotal.get("total"));
        }

        log.debug("return 教师建课统计map");
        return buildCourse;
    }

    /**
     * 按照课程统计资源总数
     * @param specId 系部ID
     * @return map
     *
     * @author cx
     * @since 2014-12-02
     */
    @Cacheable(value = "teachingSystem:cache:count", key = "'course:'.concat(#specId)")
    public List<CourseStatistics> getRscTotalForCourse(final long specId) {
        //获取指定专业的课程ID和名称
        List<CourseStatistics> courseInfoList = courseDao.getListForSpec(specId);
        if (courseInfoList.isEmpty()) {
            log.debug("专业下暂无课程:courseInfoList.isEmpty()");
            throw new ServiceException("暂无课程统计信息");
        }

        //获取课程的资源统计数量
        Map<Long, Long> courseToRsc = resourceService.countForCourse(specId);

        log.debug("循环课程统计信息");
        //组合课程资源的统计信息
        for (CourseStatistics courseStatistics : courseInfoList) {
            //获取资源数量
            Long rscTotal = courseToRsc.get(courseStatistics.getId());

            courseStatistics.setRscTotal(rscTotal == null ? 0 : rscTotal);
        }

        log.debug("return 课程统计信息");
        return courseInfoList;
    }

    /**
     * 判断指定专业下是否含有课程数据
     * @param specialtyId 专业编号
     * @return Boolean
     *
     * @author fanruiping
     * @since 2014-12-12
     */
    public Boolean checkHasCourseBySpecId(long specialtyId){
        log.debug("check specialty has course");

        return courseDao.checkBySpecialtyId(specialtyId);
    }

    /**
     * 获取指定教师的课程列表信息
     * @param pageNum 页码
     * @param pageSize 每页条数
     * @param kind 课程种类
     * @param teacherId 教师ID
     * @param keyword 搜索关键字
     * @return Page
     *
     * @author cx
     * @since 2014-12-11
     */
    public Page<Course> getAllForTeacher(final int pageNum, final int pageSize, final int kind, final long teacherId, String keyword) {
        //根据教师ID获取所教课程的ID集合
        List<Long> courseIdList = teacherTeamDao.getCourseIds(teacherId);

        if (courseIdList.isEmpty()) { //当前教师没有教课
            log.debug("教师教课ID集合为空:courseIdList.isEmpty()");
            return new Page<>();
        }

        //拆分关键字
        String[] keywords = StringUtils.split(keyword);

        //获取课程的信息
        Page<Course> courseListPage = courseDao.getList(pageNum, pageSize, kind, keywords, courseIdList);
        //获取课程的教师团队信息
        Map<Long, List<TeacherTeam>> teams = teacherTeamService.getTeams(courseIdList);

        log.debug("循环绑定课程的教师团队信息");
        for (Course course : courseListPage.getItems()) {
            course.setTeam(teams.get(course.getId()));
        }

        log.debug("return 课程分页List");
        return courseListPage;
    }

    /**
     * 获取首页系部的n个课程
     *
     * @author chenxin
     * @since 2015/1/10
     */
    public List<Course> getIndexList(final long deptId, final int top) {
        //获取指定系部的n个课程
        List<Course> courseList = courseDao.getTopListForDept(deptId, top);

        //生成系部ID集合
        List<Long> courseIdList = Lists.newArrayList();
        log.debug("生成系部ID集合:for (Course course : courseList) {");
        for (Course course : courseList) {
            courseIdList.add(course.getId());
        }

        //获取课程的教师团队信息
        Map<Long, List<TeacherTeam>> teamMap = teacherTeamService.getTeams(courseIdList);
        //绑定课程的教师团队信息
        log.debug("绑定课程的教师团队信息: for (Course course : courseList) {");
        for (Course course : courseList) {
            course.setTeam(teamMap.get(course.getId()));
        }

        log.debug("获取首页系部的n个课程");
        return courseList;
    }

    /**
     * 新增课程
     * @param course 课程实体类
     *
     * @author cx
     * @since 2014-12-11
     */
    @Transactional
    public void save(Course course) {
        //设置创建课程的用户
        course.setCreatorId(webContext.getUserId());

        //获取系部、专业名称和创建人名称
        Department department = departmentDao.getById(course.getDeptId());
        Specialty specialty = specialtyDao.getById(course.getSpecId());
        Teacher teacher = teacherDao.getById(webContext.getUserId());

        if (null == department || null == specialty || null == teacher) {
            log.debug("null == department || null == specialty || null == teacher");
            log.warn("系部、专业、创建人信息有误:deptId->{},specId->{},creatorId->{}", course.getDeptId(),
                    course.getSpecId(), webContext.getUserId());
            throw new ServiceException();
        }

        course.setDeptName(department.getName());
        course.setSpecName(specialty.getName());
        course.setCreatorName(teacher.getName());

        //保存课程
        if (!courseDao.save(course)) {
            log.debug("保存课程失败:!courseDao.save(course)");
            log.warn("课程保存失败:return false");
            throw new ServiceException();
        }

        //保存课程负责人到教师团队
        if (!teacherTeamDao.save(course.getId(), course.getCreatorId(), 0)) {
            log.debug("教师团队保存课程负责人失败:!teacherTeamDao.save(course.getId(), course.getCreatorId(), 0)");
            log.warn("教师团队保存课程负责人失败:return false");
            throw new ServiceException();
        }

        //新增课程概况记录
        if (!courseOverviewDao.save(course.getId())) {
            log.debug("新增课程概况记录失败:!courseOverviewDao.save(course.getId())");
            log.warn("新增课程概况记录失败:return false");
            throw new ServiceException();
        }

        //新增课程章节根节点
        Chapter chapter = new Chapter();
        chapter.setName(course.getName());
        chapter.setParentId(Long.valueOf(0));
        chapter.setCourseId(course.getId());
        if (!chapterDao.save(chapter)) {
            log.debug("新增课程章节根节点失败:!chapterDao.save(chapter)");
            log.warn("新增课程章节根节点失败:return false");
            throw new ServiceException();
        }
    }

    /**
     * 编辑保存课程
     * @param course 课程实体类
     *
     * @author cx
     * @since 2014-12-12
     */
    public void update(Course course) {
        //判断当前用户是否有权更新课程
        teacherTeamService.isMemeber(course.getId());

        //获取系部、专业名称
        Department department = departmentDao.getById(course.getDeptId());
        Specialty specialty = specialtyDao.getById(course.getSpecId());

        if (null == department || null == specialty) {
            log.debug("null == department || null == specialty");

            log.warn("系部、专业、创建人信息有误:deptId->{},specId->{}", course.getDeptId(), course.getSpecId());
            throw new ServiceException();
        }

        course.setDeptName(department.getName());
        course.setSpecName(specialty.getName());

        if (!courseDao.update(course)) {
            log.debug("编辑课程:return false:!courseDao.update(course)");
            log.warn("编辑保存课程失败:return false");
            throw new ServiceException();
        }

        if (!chapterDao.updateRootName(course.getName(), course.getId())) {
            log.warn("编辑保存课程失败:return false");
            throw new ServiceException();
        }
    }

    /**
     * 获取课程
     * @param courseId 课程ID
     * @return Course 课程实体类
     *
     * @author cx
     * @since 2014-12-12
     */
    public Course getById(final long courseId) {
        Course course = courseDao.getById(courseId);
        if (null == course) {
            log.debug("null == course");
            log.warn("课程信息为null:courseId->" + courseId);
            throw new ServiceException("未找到指定课程信息");
        }

        log.debug("return 课程明细信息");
        return course;
    }

    /**
     * 更新课程封面
     * @param coverUrl 封面路径
     * @param courseId 课程ID
     *
     * @author cx
     * @since 2014-12-13
     */
    public void updateCover(final String coverUrl, final long courseId) {
        //查询获取原先的课程封面路径
        String originalUrl = courseDao.getCover(courseId);
        //更新课程封面路径
        if (!courseDao.updateCover(courseId, coverUrl)) {
            log.debug("更新课程封面路径失败:!courseDao.updateCover(courseId, coverUrl)");

            log.warn("更改封面路径失败:courseId->" + courseId + ",url->" + coverUrl);

            //删除上传的文件
            File coverFile = new File(webContext.getRootDirPath(), coverUrl);
            FileUtils.deleteQuietly(coverFile);

            throw new ServiceException();
        }

        //删除封面图片
        if (originalUrl != null) {
            log.debug("originalUrl != null");
            File originalFile = new File(webContext.getRootDirPath(), originalUrl);
            FileUtils.deleteQuietly(originalFile);
        }
    }

    /**
     * 获取课程列表（授权课程负责人）
     * @param pageNum 页码
     * @param pageSize 每页条数
     * @param specId 专业ID
     * @param keyword 搜索关键字
     * @return Page
     *
     * @author cx
     * @since 2014-12-15
     */
    public Page<Course> getListForAuth(final int pageNum, final int pageSize, final long specId, String keyword) {
        List<Long> specIdList = Lists.newArrayList();
        if (0 == specId) {
            log.debug("0 == specId");

            if (Roles.DEPT_ADMIN == webContext.getRole()) {
                log.debug("Roles.DEPT_ADMIN == webContext.getRole()");

                //系部管理员登录，获取系部所有的专业ID
                specIdList = specialtyDao.getIdsForDeptLeader(webContext.getUserId());
            }
            else if (Roles.SPEC_LEADER == webContext.getRole()) {
                log.debug("Roles.SPEC_LEADER == webContext.getRole()");

                //专业负责人登录，获取其所负责的专业ID
                specIdList = specialtyDao.getIdsForLeader(webContext.getUserId());
            }
        }
        else {
            log.debug("0 != specId");
            specIdList.add(specId);
        }

        if ((Roles.DEPT_ADMIN == webContext.getRole() || Roles.SPEC_LEADER == webContext.getRole()) && specIdList.isEmpty()) {
            log.debug("用户没有负责的专业:(Roles.DEPT_ADMIN == webContext.getRole() || Roles.SPEC_LEADER == webContext.getRole()) && specIdList.isEmpty()");

            log.info("登录用户没有负责的专业:userId->{}", webContext.getUserId());
            return new Page<>();
        }

        //拆分搜查关键字
        String[] keywords = StringUtils.split(keyword);
        //获取分页课程列表
        Page<Course> coursePage = courseDao.getListForSpec(pageNum, pageSize, specIdList, keywords);

        log.debug("return 课程分页集合");
        return coursePage;
    }

    /**
     * 更新课程层级结构
     * @param course 课程实体类
     *
     * @author cx
     * @since 2014-12-16
     */
    public void updateCourseStruct(Course course) {
        //判断用户是否在课程的教师团队
        teacherTeamService.isMemeber(course.getId());

        if (!courseDao.updateStruct(course)) {
            log.debug("更新层级结构失败:!courseDao.updateStruct(course)");
            log.warn("更新层级结构失败:courseId->" + course.getId());
            throw new ServiceException();
        }
    }

    /**
     * 获取课程的层级结构
     *
     * @author cx
     * @since 2014-12-16
     */
    public Course getCourseStruct(final long courseId) {
        //获取结构名称
        Course course = courseDao.getStruct(courseId);
        if (course == null) {
            log.debug("course == null");
            throw new ServiceException("未获取到不存在的信息");
        }

        log.debug("return 层级结构");
        return course;
    }

    /**
     * 获取首页专业下的课程集合
     *
     * @author chenxin
     * @since 2015/1/11
     */
    @Cacheable(value = "teachingSystem:cache:index", key = "'spec:courses:'.concat(#specId).concat(#top)")
    public List<CourseStatistics> getIndexListOfSpec(final long specId, final int top) {
        //获取指定专业下的前n个课程信息
        List<CourseStatistics> courseInfoList = courseDao.getTopListForSpec(specId, top);

        //生成课程ID集合
        Set<Long> courseIdSet = Sets.newHashSet();
        log.debug("生成课程ID集合:for (CourseStatistics courseInfo : courseInfoList) {");
        for (CourseStatistics courseInfo : courseInfoList) {
            courseIdSet.add(courseInfo.getId());
        }

        //获取课程的课程资源总数
        Map<Long, Long> rscMap = resourceService.countForCourses(courseIdSet);

        //绑定课程的课程资源总数
        log.debug("绑定课程的课程资源总数:for (CourseStatistics courseInfo : courseInfoList) {");
        for (CourseStatistics courseInfo : courseInfoList) {
            courseInfo.setRscTotal(rscMap.get(courseInfo.getId()));
        }

        log.debug("获取首页专业下的课程集合");
        return courseInfoList;
    }

    /**
     * 发布课程
     * @param courseId 课程编号
     * @return boolean
     *
     * @author fanruiping
     * @since 2015-02-05
     */
    public boolean release(long courseId){
        //设置发布
        if(courseDao.setRelease(courseId) > 0){
            log.debug("课程发布成功:courseDao.setRelease(courseId) > 0");

            return true;
        }

        log.debug("课程发布失败");

        return false;
    }

    /**
     * 获取教师或学生的
     * @param userInfo 登录用户信息
     * @param right 权限（1：根据角色获取、0：不区分角色获取全部）
     * @return List
     *
     * @author fanruiping
     * @since 2015-04-06
     */
    public List<Course> getListForTeacherOrStudent(User userInfo, int right) {
        List<Course> courses = new ArrayList<>();

        //判断获取数据类型为不限制角色或登录用户角色为超级管理员
        if (0 == right || userInfo.getRole() == Roles.SUPER_ADMIN) {
            log.debug("获取数据类型为不限制角色或登录用户角色为超级管理员:1 == right || userInfo.getRole() == Roles.SUPER_ADMIN");

            courses = courseDao.getAll();
        } else if (userInfo.getRole() != Roles.STUDENT) {
            log.debug("登录用户角色为教师:userInfo.getRole() != Roles.STUDENT");

            //课程教师团队里获取
            List<Course> hasCourses = courseDao.getListForTeacher(userInfo.getUserId());

            //课程群组负责人负责的课程群组代码集合
            List<String> courseCodes = courseGroupDao.getCodesByTeacher(userInfo.getNumber());

            if(!courseCodes.isEmpty()){
                //课程群组负责人获取
                List<Course> courseList = courseDao.getListByCodes(courseCodes);

                hasCourses.addAll(courseList);

                //编号集合
                Set<Long> ids = new HashSet<>();

                //循环集合删除重复数据
                for (int i = 0; i < hasCourses.size(); i++){
                    if(!ids.contains(hasCourses.get(i).getId())){
                        courses.add(hasCourses.get(i));
                    }

                    ids.add(hasCourses.get(i).getId());
                }

                hasCourses.clear();
                ids.clear();
            }
        } else if (userInfo.getRole() == Roles.STUDENT) {
            log.debug("当前登录用户角色为学生:userInfo.getRole() == Roles.STUDENT");

            List<Long> courseIds = courseCodeDao.getListForStudent(userInfo.getNumber());

            //判断课程编号集合不为空
            if (!courseIds.isEmpty()) {
                log.debug("课程编号集合不为空:!courseIds.isEmpty()");

                courses = courseDao.getByIds(courseIds);
            }
        }

        return courses;
    }

    /**
     * 学生在学课程
     * @param type 1必修课 2选修课
     * @param studentId 学生ID
     * @param page 页数
     * @param pageSize 条数
     * @return page
     *
     * @author wyh
     * @since 2015-04-02
     */
    public Page<Course> listStudentCourse(int type, long studentId, int page, int pageSize){
        if(webContext.getUserId() != studentId && webContext.getRole() != Roles.STUDENT){
            log.debug("登录非学生本人：webContext.getUserId() != studentId && webContext.getRole() != Roles.STUDENT");
            log.warn("权限错误->{}", studentId);
            throw new ServiceException("非法权限");
        }

        //根据学生ID获得学号，再查询选课代码List
        List<String> csCodeList = studentCourseDao.getCourseCodes(studentDao.getNumById(studentId));

        //保存匹配的课程IDList
        List<Long> courseIdList = new ArrayList<>();
        Page<Course> coursePage = courseDao.listStudentCourse(type, csCodeList, page, pageSize, courseIdList);

        //查询课程团队教师
        List<TeacherTeam> teamList = new ArrayList<>();
        if(!courseIdList.isEmpty()) {
            log.debug("课程id集合不为空：!courseIdList.isEmpty()");
            teamList = teacherTeamDao.getTeamByIds(courseIdList);
        }

        //循环set教师团队教师
        log.debug("循环set教师团队教师:for(Course course : coursePage.getItems())");
        for(Course course : coursePage.getItems()){
            course.setTeam(new ArrayList<TeacherTeam>());
        }
        for(TeacherTeam team : teamList){
            for(Course course : coursePage.getItems()){
                if(team.getCourseId() == course.getId()){
                    course.getTeam().add(team);
                    break;
                }
            }
        }

        log.debug("返回 学生在学课程");
        return coursePage;
    }


 	/**
     * 根据课程代码获取课程ＩＤ、课程名称
     *
     * @param courseCode　课程代码
     * @return　List<Map<String, Object>>
     * @author lyj
     * @since 2015-3-31
     */
    public Map<String, Map<String, Object>> getIdName(final Set<String> courseCode) {

        List<Map<String, Object>> maps = courseDao.getIdName(courseCode);

        Map<String, Map<String,Object>> reMap = new HashMap<>();

        //将结果转换为“课程代码->Map”的格式
        log.debug("将结果转换为“课程代码->Map”的格式");
        for (Map<String, Object> map : maps) {
            reMap.put(map.get("courseCode").toString(), map);
        }

        log.debug("根据课程代码获取课程ＩＤ、课程名称");
        return reMap;

    }
}
